package com.xyy.saas.payment.service.report.impl;

import com.alibaba.fastjson.JSON;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.xyy.saas.payment.constants.Constants;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.pojo.bo.ReportPropertiesBo;
import com.xyy.saas.payment.pojo.bo.ReportQueryOrderBo;
import com.xyy.saas.payment.pojo.bo.ReportQueryOrderViewBo;
import com.xyy.saas.payment.pojo.bo.SelectorBo;
import com.xyy.saas.payment.pojo.dto.ConvertReport;
import com.xyy.saas.payment.pojo.dto.ReportQueryOrderDto;
import com.xyy.saas.payment.service.report.ReportService;
import com.xyy.saas.payment.pojo.vo.ReportExportOrderVo;
import com.xyy.saas.payment.pojo.vo.ReportQueryOrderVo;
import com.xyy.saas.payment.service.OrderService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.JSONUtils;
import com.xyy.saas.payment.util.PageUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author wangtianqi
 * @date 2020/5/11 10:12
 */
@Service
@DependsOn({"apolloUtil", "orderServiceImpl"})
public class ReportServiceImpl implements ReportService, InitializingBean {

    private static Logger LOGGER = LoggerFactory.getLogger(ReportServiceImpl.class);

    private List<SelectorBo> payModes = JSONUtils.parse2List(ApolloUtil.getProperty(Constants.APOLLO_KEY_PAY_MODES), SelectorBo.class);
    private List<SelectorBo> businessOrderTypes = JSONUtils.parse2List(ApolloUtil.getProperty(Constants.APOLLO_KEY_BUSINESS_ORDER_TYPES), SelectorBo.class);

    @Autowired
    private OrderService orderServiceImpl;

    @Override
    public ReportPropertiesBo getReportProperties() {
        ReportPropertiesBo bo = new ReportPropertiesBo();
        bo.setPayModes(payModes);
        bo.setOrderType(Arrays.asList(new SelectorBo(Constants.PAY, Constants.ORDER_TYPE_PAY), new SelectorBo(Constants.REFUND, Constants.ORDER_TYPE_REFUND)));
        bo.setBusinessOrderTypes(businessOrderTypes);
        return bo;
    }

    @Override
    public ReportQueryOrderBo reportQueryOrder(ReportQueryOrderVo reportQueryOrderVo) {
        ReportQueryOrderBo reportQueryOrderBo = new ReportQueryOrderBo();
        if(Constants.PAY.equals(reportQueryOrderVo.getOrderType())) {
            reportQueryOrderBo = selectPay(reportQueryOrderVo);
        }
        if(Constants.REFUND.equals(reportQueryOrderVo.getOrderType())) {
            reportQueryOrderBo = selectRefund(reportQueryOrderVo);
        }
        return reportQueryOrderBo;
    }

    @Override
    public List<ReportQueryOrderViewBo> reportExportOrderVo(ReportExportOrderVo reportExportOrderVo) throws PaymentException {
        List<ReportQueryOrderViewBo> result = new ArrayList<>();
        if(Constants.PAY.equals(reportExportOrderVo.getOrderType())) {
            if(reportExportOrderVo.getBusinessOrderNo() != null) {
                List<PrePayOrder> prePayOrders = orderServiceImpl.getPrePayOrderByBusinessOrderNo(reportExportOrderVo.getBusinessOrderNo());
                for(PrePayOrder prePayOrder : prePayOrders) {
                    List<PayResultOrder> payResultOrders = orderServiceImpl.selectPayResultOrderListByPayNo(prePayOrder.getPayNo());
                    for(PayResultOrder payResultOrder : payResultOrders) {
                        result.add(convertReportQueryOrderViewBo(payResultOrder, prePayOrder));
                    }
                }
            } else {
                ReportQueryOrderDto dto = ConvertReport.convertReportQueryOrderDto(reportExportOrderVo, null, null);
                int totalCount = selectPayCountByCondition(dto);
                if (totalCount > Constants.DATA_MAX) {
                    LOGGER.warn("ReportServiceImpl#reportExportOrderVo data count too much count:{} reportExportOrderVo:{}", totalCount , JSON.toJSONString(reportExportOrderVo));
                    throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
                }
                int pageSize = 200;
                int pageSum = PageUtil.getPageSum(totalCount, pageSize);
                for (int pageNum = 1; pageNum <= pageSum; pageNum++) {
                    ReportQueryOrderDto reportQueryOrderDto = ConvertReport.convertReportQueryOrderDto(reportExportOrderVo, pageNum, 200);
                    LOGGER.info("ReportServiceImpl#reportExportOrderVo data page:{} pageSum:{} reportExportOrderVo:{}", pageNum , pageSum, JSON.toJSONString(reportExportOrderVo));
                    result.addAll(selectPayByCondition(reportQueryOrderDto));
                }
            }
        }
        if(Constants.REFUND.equals(reportExportOrderVo.getOrderType())) {
            if(reportExportOrderVo.getBusinessOrderNo() != null) {
                List<PrePayOrder> prePayOrders = orderServiceImpl.getPrePayOrderByBusinessOrderNo(reportExportOrderVo.getBusinessOrderNo());
                for(PrePayOrder prePayOrder : prePayOrders) {
                    List<RefundOrder> refundOrders = orderServiceImpl.selectRefundOrderListByPayNo(prePayOrder.getPayNo());
                    for(RefundOrder refundOrder : refundOrders) {
                        result.add(convertReportQueryOrderViewBo(refundOrder, prePayOrder));
                    }
                }
            } else {
                ReportQueryOrderDto dto = ConvertReport.convertReportQueryOrderDto(reportExportOrderVo, null, null);
                int totalCount = selectRefundCountByCondition(dto);
                int pageSize = 200;
                int pageSum = PageUtil.getPageSum(totalCount, pageSize);
                for (int pageNum = 1; pageNum <= pageSum; pageNum++) {
                    ReportQueryOrderDto reportQueryOrderDto = ConvertReport.convertReportQueryOrderDto(reportExportOrderVo, pageNum, 200);
                    LOGGER.info("ReportServiceImpl#reportExportOrderVo data page:{} pageSum:{} reportExportOrderVo:{}", pageNum , pageSum, JSON.toJSONString(reportExportOrderVo));
                    result.addAll(selectRefundByCondition(reportQueryOrderDto));
                }
            }
        }
        return result;
    }

    /**
     * 查询支付单
     * @param reportQueryOrderVo
     * @return
     */
    private ReportQueryOrderBo selectPay(ReportQueryOrderVo reportQueryOrderVo){
        List<ReportQueryOrderViewBo> result = new ArrayList<>();
        ReportQueryOrderBo reportQueryOrderBo = new ReportQueryOrderBo();
        int count = 0;
        if(reportQueryOrderVo.getBusinessOrderNo() != null) {
            List<PrePayOrder> prePayOrders = orderServiceImpl.getPrePayOrderByBusinessOrderNo(reportQueryOrderVo.getBusinessOrderNo());
            for(PrePayOrder prePayOrder : prePayOrders) {
                List<PayResultOrder> payResultOrders = orderServiceImpl.selectPayResultOrderListByPayNo(prePayOrder.getPayNo());
                for(PayResultOrder payResultOrder : payResultOrders) {
                    result.add(convertReportQueryOrderViewBo(payResultOrder, prePayOrder));
                    count++;
                }
            }
        } else {
            ReportQueryOrderDto reportQueryOrderDto = ConvertReport.convertReportQueryOrderDto(reportQueryOrderVo);
            result = selectPayByCondition(reportQueryOrderDto);
            count = selectPayCountByCondition(reportQueryOrderDto);
        }
        reportQueryOrderBo.setDataCount(count);
        reportQueryOrderBo.setReportQueryOrderViewBos(result);
        return reportQueryOrderBo;
    }


    /**
     * 查询已完成的退款单
     * @param reportQueryOrderVo
     * @return
     */
    private ReportQueryOrderBo selectRefund(ReportQueryOrderVo reportQueryOrderVo){
        List<ReportQueryOrderViewBo> result = new ArrayList<>();
        ReportQueryOrderBo reportQueryOrderBo = new ReportQueryOrderBo();
        int count = 0;
        if(reportQueryOrderVo.getBusinessOrderNo() != null) {
            List<PrePayOrder> prePayOrders = orderServiceImpl.getPrePayOrderByBusinessOrderNo(reportQueryOrderVo.getBusinessOrderNo());
            for(PrePayOrder prePayOrder : prePayOrders) {
                List<RefundOrder> refundOrders = orderServiceImpl.selectRefundOrderListByPayNo(prePayOrder.getPayNo());
                for(RefundOrder refundOrder : refundOrders) {
                    result.add(convertReportQueryOrderViewBo(refundOrder, prePayOrder));
                    count++;
                }
            }
        } else {
            ReportQueryOrderDto reportQueryOrderDto = ConvertReport.convertReportQueryOrderDto(reportQueryOrderVo);
            result = selectRefundByCondition(reportQueryOrderDto);
            count = selectRefundCountByCondition(reportQueryOrderDto);
        }
        reportQueryOrderBo.setDataCount(count);
        reportQueryOrderBo.setReportQueryOrderViewBos(result);
        return reportQueryOrderBo;
    }

    public ReportQueryOrderViewBo convertReportQueryOrderViewBo(PayResultOrder payResultOrder, PrePayOrder prePayOrder) {
        ReportQueryOrderViewBo reportQueryOrderViewBo = new ReportQueryOrderViewBo();
        reportQueryOrderViewBo.setAmount(convertAmount(payResultOrder.getAmount()));
        reportQueryOrderViewBo.setBusinessOrderNo(prePayOrder.getBusinessOrderNo());
        reportQueryOrderViewBo.setBusinessOrderType(getBusinessOrderTypesDesc(payResultOrder.getBusinessOrderType()));
        reportQueryOrderViewBo.setChannelTransactionNo(payResultOrder.getChannelTransactionNo());
        reportQueryOrderViewBo.setOrderType(Constants.ORDER_TYPE_PAY);
        reportQueryOrderViewBo.setPayer(payResultOrder.getPayer());
        reportQueryOrderViewBo.setPayMode(getPayModeDesc(payResultOrder.getPayChannel()));
        reportQueryOrderViewBo.setPayNo(payResultOrder.getPayNo());
        reportQueryOrderViewBo.setPayTime(DateUtil.format(payResultOrder.getCtime(), DateUtil.DATEFORMATSECOND));
        return reportQueryOrderViewBo;
    }

    public ReportQueryOrderViewBo convertReportQueryOrderViewBo(RefundOrder refundOrder, PrePayOrder prePayOrder) {
        ReportQueryOrderViewBo reportQueryOrderViewBo = new ReportQueryOrderViewBo();
        reportQueryOrderViewBo.setAmount(convertAmount(0 - refundOrder.getAmount()));
        reportQueryOrderViewBo.setBusinessOrderNo(prePayOrder.getBusinessOrderNo());
        reportQueryOrderViewBo.setBusinessOrderType(getBusinessOrderTypesDesc(prePayOrder.getBusinessOrderType()));
        reportQueryOrderViewBo.setChannelTransactionNo(refundOrder.getChannelTransactionNo());
        reportQueryOrderViewBo.setOrderType(Constants.ORDER_TYPE_REFUND);
        reportQueryOrderViewBo.setPayer(refundOrder.getPayer());
        reportQueryOrderViewBo.setPayMode(getPayModeDesc(refundOrder.getChannel()));
        reportQueryOrderViewBo.setPayNo(refundOrder.getPayNo());
        reportQueryOrderViewBo.setPayTime(DateUtil.format(refundOrder.getSuccessTime(), DateUtil.DATEFORMATSECOND));
        return reportQueryOrderViewBo;
    }

    private String convertAmount(int amount) {
        return new BigDecimal(amount).divide(new BigDecimal(100)).toString();
    }


    private String getPayModeDesc(String payMode) {
        if(payMode == null) {
            return null;
        }
        for(SelectorBo bo : payModes) {
            if(bo.getValue().equals(payMode)) {
                return bo.getDesc();
            }
        }
        return payMode;
    }


    private String getBusinessOrderTypesDesc(String businessOrderType) {
        if(businessOrderType == null) {
            return null;
        }
        for(SelectorBo bo : businessOrderTypes) {
            if(bo.getValue().equals(businessOrderType)) {
                return bo.getDesc();
            }
        }
        return businessOrderType;
    }

    /**
     * 根据条件查询支付单
     * @param reportQueryOrderDto
     * @return
     */
    private List<ReportQueryOrderViewBo> selectPayByCondition(ReportQueryOrderDto reportQueryOrderDto) {
        List<ReportQueryOrderViewBo> result = new ArrayList<>();
        List<PayResultOrder> payResultOrders =
                orderServiceImpl.selectPayResultOrderListByCondition(reportQueryOrderDto.getTradeNo(),reportQueryOrderDto.getBusinessOrderType(),
                        reportQueryOrderDto.getPayer(),reportQueryOrderDto.getChannelTransactionNo(),
                        reportQueryOrderDto.getPayMode(),
                        reportQueryOrderDto.getBeginDate(),reportQueryOrderDto.getEndDate(), reportQueryOrderDto.getPage(), reportQueryOrderDto.getPageSize());
        for(PayResultOrder payResultOrder : payResultOrders) {
            PrePayOrder prePayOrder = orderServiceImpl.getPrePayOrderByPayNo(payResultOrder.getPayNo());
            result.add(convertReportQueryOrderViewBo(payResultOrder, prePayOrder));
        }
        return result;
    }

    /**
     * 根据条件查询已完成的退款单
     * @param reportQueryOrderDto
     * @return
     */
    private List<ReportQueryOrderViewBo> selectRefundByCondition(ReportQueryOrderDto reportQueryOrderDto) {
        List<ReportQueryOrderViewBo> result = new ArrayList<>();
        List<RefundOrder> refundOrders =
                orderServiceImpl.selectRefundOrderListByCondition(reportQueryOrderDto.getTradeNo(),reportQueryOrderDto.getBusinessOrderType(),
                        reportQueryOrderDto.getPayer(),reportQueryOrderDto.getChannelTransactionNo(),
                        reportQueryOrderDto.getPayMode(),
                        reportQueryOrderDto.getBeginDate(), reportQueryOrderDto.getEndDate(), reportQueryOrderDto.getPage(), reportQueryOrderDto.getPageSize());
        for(RefundOrder refundOrder : refundOrders) {
            PrePayOrder prePayOrder = orderServiceImpl.getPrePayOrderByPayNo(refundOrder.getPayNo());
            result.add(convertReportQueryOrderViewBo(refundOrder, prePayOrder));
        }
        return result;
    }


    private int selectPayCountByCondition(ReportQueryOrderDto reportQueryOrderDto) {
        int count =
                orderServiceImpl.selectPayResultOrderCountByCondition(reportQueryOrderDto.getTradeNo(),reportQueryOrderDto.getBusinessOrderType(),
                        reportQueryOrderDto.getPayer(),reportQueryOrderDto.getChannelTransactionNo(),
                        reportQueryOrderDto.getPayMode(),
                        reportQueryOrderDto.getBeginDate(),reportQueryOrderDto.getEndDate());
        return count;
    }

    private int selectRefundCountByCondition(ReportQueryOrderDto reportQueryOrderDto) {
        int count =
                orderServiceImpl.selectRefundOrderCountByCondition(reportQueryOrderDto.getTradeNo(),reportQueryOrderDto.getBusinessOrderType(),
                        reportQueryOrderDto.getPayer(),reportQueryOrderDto.getChannelTransactionNo(),
                        reportQueryOrderDto.getPayMode(),
                        reportQueryOrderDto.getBeginDate(),reportQueryOrderDto.getEndDate());
        return count;
    }

    @Override
    public String buildExcelFileName(ReportExportOrderVo reportExportOrderVo) {
        String timeTitle = "";
        if(reportExportOrderVo.getBeginDate() != null && reportExportOrderVo.getEndDate() != null) {
            String beginDate = DateUtil.getDayByDate(reportExportOrderVo.getBeginDate());
            String endDate = DateUtil.getDayByDate(reportExportOrderVo.getEndDate());
            timeTitle = beginDate + "-" + endDate;
        }
        String businessOrderType = reportExportOrderVo.getBusinessOrderType() == null ? "" : getBusinessOrderTypesDesc(reportExportOrderVo.getBusinessOrderType());
        String payMode = reportExportOrderVo.getPayMode() == null ? "" : getPayModeDesc(reportExportOrderVo.getPayMode());
        String orderType = reportExportOrderVo.getOrderType().equals(Constants.PAY) ? Constants.ORDER_TYPE_PAY : Constants.ORDER_TYPE_REFUND;
        List<String> names = new ArrayList<>();
        names.add(timeTitle);
        names.add(businessOrderType);
        names.add(orderType);
        names.add(payMode);
        String fileName = "";
        for(String name : names) {
            if(!StringUtils.isEmpty(name)) {
                if(StringUtils.isEmpty(fileName)) {
                    fileName = name;
                } else {
                    fileName = fileName + "-" + name;
                }
            }
        }
        return fileName + ".xlsx";
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        Set set = new HashSet();
        set.add(Constants.APOLLO_KEY_PAY_MODES);
        set.add(Constants.APOLLO_KEY_BUSINESS_ORDER_TYPES);
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                payModes = JSONUtils.parse2List(ApolloUtil.getProperty(Constants.APOLLO_KEY_PAY_MODES), SelectorBo.class);
                businessOrderTypes = JSONUtils.parse2List(ApolloUtil.getProperty(Constants.APOLLO_KEY_BUSINESS_ORDER_TYPES), SelectorBo.class);
                LOGGER.info("ReportServiceImpl#onChange change payModes businessOrderTypes success payModes:{} businessOrderTypes:{}" , JSONUtils.toJSON(payModes), JSONUtils.toJSON(businessOrderTypes));
            }
        }, set);
    }

}
